package com.kaguya.observer;

import java.util.LinkedList;

/**
 * 行为型：观察者模式，（发布-订阅模式）
 * 目的：一方的状态值发生改变的时候，同时所有的消息订阅者，让他们收到消息后自主更新。（示例：微信公众号，发布者发布文章后，订阅者得到通知）
 * 主要结构：抽象发布者，发布者实现，抽象订阅者，订阅者实现
 * 抽象发布者和抽象订阅者目的都是为了约束其具体实例
 * 发布者保存了订阅者的列表，提供了一个发布方法以通知订阅者，执行所有订阅者的消息通知方法，订阅者只要订阅了发布者，在其发布消息后响应式执行更新方法
 */
public class PubSub {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个发布者
        Weather publish = new Weather();
        // 创建订阅者并订阅该发布者
        Observer cView = new CView();
        Observer wearView = new WearView();

        publish.attachObserver(cView);
        publish.attachObserver(wearView);
        // 发布者调用业务方法，对订阅者进行通知
        int loop = 3;
        while (loop > 0) {
            publish.getWeather();
            Thread.sleep(3000);
            loop--;
        }
        // 订阅者收到通知执行相应处理
        System.out.println("---------------------");
        // 删除一个订阅者并打印所有订阅者
        publish.detachObserver(wearView);
        publish.showAllObservers();
    }
}

/**
 * 发布者抽象（发布者）
 */
abstract class Subject {

    // 消息订阅者列表，保存所有已经订阅了的订阅者
    private static LinkedList<Observer> observers = new LinkedList<>();

    /**
     * 增加一个(观察)消息订阅者
     */
    public void attachObserver(Observer observer) {
        observers.add(observer);
    }

    /**
     * 删除一个(观察)消息订阅者
     */
    public void detachObserver(Observer observer) {
        observers.remove(observer);
    }

    /**
     * 获取所有订阅者
     */
    public void showAllObservers() {
        observers.forEach(System.out::println);
    }

    /**
     * 通知所有(观察)消息订阅者
     */
    public void notifyObservers() {
        // 调用所有的订阅者的update方法，其订阅者的实现自己实现update的细节，对发布者的更新进行处理
        for (Observer obs : observers) {
            obs.update(this);
        }
    }
}
/**
 * 观察者抽象 （订阅者）
 */
interface Observer {
    /**
     * 消息发布者通知订阅者的回调
     * @param subject
     */
    void update(Subject subject);
}

/**
 * 发布者实现
 */
class Weather extends Subject {

    private float temp;

    public Weather() {
        this.temp = -10000;
    }

    public float getTemperature() {
        return this.temp;
    }

    /**
     * 模拟发布者进行某个业务后对订阅者进行通知
     * 更新温度
     */
    public void getWeather() {
        float max = 60,min = -80;
        float newTemp = (float) (Math.random() * max - min + min);

        if(newTemp != this.temp) {
            this.temp = newTemp;
            // 调用发布者通知方法
            super.notifyObservers();
        }
    }

}

/**
 * 订阅者1实现
 */
class CView implements Observer {

    @Override
    public void update(Subject subject) {
        if(subject instanceof Weather) {
            System.out.println("当前摄氏温度："+ ((Weather) subject).getTemperature());
        }
    }
}
/**
 * 订阅者2实现
 */
class WearView implements Observer {

    @Override
    public void update(Subject subject) {
        if(subject instanceof Weather) {
            Weather weather = (Weather) subject;
            float temperature = weather.getTemperature();

            if(temperature < 10) {
                System.out.println("穿衣建议：十分寒冷，请注意保暖");
            } else if (temperature < 20) {
                System.out.println("穿衣建议：天气凉爽，注意防寒");
            } else if (temperature < 30) {
                System.out.println("穿衣建议：比较温暖，可以穿少点");
            } else {
                System.out.println("穿衣建议：天气炎热，注意避暑");
            }
        }
    }
}